#include "AMFWriter.h"
#include <wx/mstream.h>

namespace AMF {

AMFWriter::AMFWriter(wxOutputStream *output)
{
    _output = output;
}

AMFWriter::~AMFWriter()
{
    //dtor
}

char *ConvChar(const wxString *value)
{
    char *data = new char[value->Len() * 3];
    strcpy(data, (const char*)value->mb_str(wxConvUTF8));
    return data;
}

void AMFWriter::WriteAMFMessage(AMFMessage *msg)
{
    //寫入AMF版本
    short ver = (short)msg->_version;
    WriteAMFInt16(&ver);

    //寫入header
    //寫入header數量
    short _headerCount = 0;
    WriteAMFInt16(&_headerCount);

    //寫入body
    //寫入body數量
    short _bodyCount = (short)msg->GetBodyCount();
    WriteAMFInt16(&_bodyCount);
    //寫入body內容
    for (int i = 0; i < _bodyCount; i++)
    {
        AMFBody *body = msg->GetBody(i);
        WriteAMFBody(body);
    }

}

void AMFWriter::WriteAMFBody(const AMFBody *value)
{
    //寫入response
    WriteAMFUntypeString(&(value->_response));

    //寫入target
    WriteAMFUntypeString(&(value->_target));

    //寫入內容
    wxMemoryOutputStream tmp;
    AMFWriter(&tmp).WriteAMFData(value->_content);
    //寫入內容長度
    int len = tmp.GetSize();
    WriteAMFInt32(&len);
    //寫入內容主體
    wxMemoryInputStream(tmp).Read(*_output);

}

void AMFWriter::WriteAMFData(const AMFObject *value)
{
    wxByte *buff = NULL;
    switch (value->_type)
    {
        case AMF0DataType::String:
            WriteAMFString((wxString*)(value->_content));
            break;
        case AMF0DataType::Array:
            WriteAMFArray((vector<AMFObject*>*)(value->_content));
            break;
        case AMF0DataType::AMF3Data:
            buff = new wxByte(AMF0DataType::AMF3Data);
            _output->Write(buff, 1);

            WriteAMF3Data((AMFObject*)(value->_content));
            break;
        default:
            throw wxString::Format(_("未支援AMF0的'%d'型態寫入"), value->_type);
    }
    delete buff;
}

void AMFWriter::WriteAMFArray(const vector<AMFObject*> *value)
{
    //寫入型態
    wxByte *type = new wxByte(AMF0DataType::Array);
    _output->Write(type, 1);
    delete type;

    //寫入陣列個數
    int count = value->size();
    WriteAMFInt32(&count);
    for (unsigned int i = 0; i < value->size(); i++)
    {
        WriteAMFData((*value)[i]);
    }
}

void AMFWriter::WriteAMF3Data(const AMFObject *value)
{
    wxByte *buff = NULL;
    switch (value->_type)
    {
        case AMF3DataType::Null:
        case AMF3DataType::True:
        case AMF3DataType::False:
            _output->Write(&(value->_type), 1);
            break;
        case AMF3DataType::Integer:
            buff = new wxByte(AMF3DataType::Integer);
            _output->Write(buff, 1);
            delete buff;

            WriteAMF3IntegerData((int*)(value->_content));
            break;
        case AMF3DataType::String:
            WriteAMF3String((wxString*)(value->_content));
            break;
        case AMF3DataType::Object:
            WriteAMF3Object((Hashtable*)(value->_content));
            break;
        default:
            throw wxString::Format(_("未支援AMF3的'%d'型態寫入"), value->_type);
    }

}

void AMFWriter::WriteAMF3Object(Hashtable *value)
{
    wxByte *type = new wxByte(AMF3DataType::Object);
    _output->Write(type, 1);
    delete type;

    int handle = 0;
    handle = handle << 1;
    handle = handle | 1;
    handle = handle << 1;
    handle = handle | 0;
    handle = handle << 2;
    handle = handle | 3;
    WriteAMF3IntegerData(&handle);
    int i = 1;
    WriteAMF3IntegerData(&i);

    Hashtable::iterator it;
    for ( it = value->begin(); it != value->end(); ++it ) {
        char *key = ConvChar(&it->first);
        int size = strlen(key);
        handle = size;
        handle = handle << 1;
        handle = handle | 1;
        WriteAMF3IntegerData(&handle);
        if (size > 0) _output->Write(key, size);
        WriteAMF3Data(it->second);
        delete []key;
    }
    WriteAMF3IntegerData(&i);
}

void AMFWriter::WriteAMFString(const wxString *value)
{
    wxByte *type = new wxByte(AMF0DataType::String);
    _output->Write(type, 1);
    delete type;

    WriteAMFUntypeString(value);
}

void AMFWriter::WriteAMFUntypeString(const wxString *value)
{
        char *data = ConvChar(value);
        int len = strlen(data);
        if(len < 65536)
        {
            wxInt16 i16 = (wxInt16)len;
            WriteAMFInt16(&i16);
            if (len > 0) _output->Write(data, len);
            delete []data;
        }
        else throw _("未支援長字串!!");
}

void AMFWriter::WriteAMFInt16(const wxInt16 *value)
{
    wxByte data[2] = {0};
    data[0] = *value >> 8;
    data[1] = *value;
    _output->Write(data, 2);
}

void AMFWriter::WriteAMFInt32(const wxInt32 *value)
{
    wxByte data[4] = {0};
    data[0] = *value >> 24;
    data[1] = *value >> 16;
    data[2] = *value >> 8;
    data[3] = *value;
    _output->Write(data, 4);
}

void AMFWriter::WriteAMF3String(const wxString *value){
    //寫入型態
    wxByte *type = new wxByte(AMF3DataType::String);
    _output->Write(type, 1);
    delete type;

    int i = 1;
    if (value->IsEmpty())
    {
        WriteAMF3IntegerData(&i);
    }
    else
    {
        char *data = ConvChar(value);
        int len = strlen(data);
        int handle = strlen(data);
        handle = handle << 1;
        handle = handle | 1;
        WriteAMF3IntegerData(&handle);
        if (len > 0) _output->Write(data, len);
        delete [] data;
    }
}

void AMFWriter::WriteAMF3IntegerData(const int *value) {
    int _value = *value & 0x1fffffff;

    wxByte buff;
    if (_value < 0x80) {
        buff = _value;
        _output->Write(&buff, 1);
    }
    else {
        if (_value < 0x4000)
        {
            buff = _value >> 7 & 0x7f | 0x80;
            _output->Write(&buff, 1);
            buff = _value & 0x7f;
            _output->Write(&buff, 1);
        }
        else {
            if (_value < 0x200000)
            {
                buff = _value >> 14 & 0x7f | 0x80;
                _output->Write(&buff, 1);
                buff = _value >> 7 & 0x7f | 0x80;
                _output->Write(&buff, 1);
                buff = _value & 0x7f;
                _output->Write(&buff, 1);
            }
            else
            {
                buff = _value >> 22 & 0x7f | 0x80;
                _output->Write(&buff, 1);
                buff = _value >> 15 & 0x7f | 0x80;
                _output->Write(&buff, 1);
                buff = _value >> 8 & 0x7f | 0x80;
                _output->Write(&buff, 1);
                buff = _value & 0xff;
                _output->Write(&buff, 1);
            }
        }
    }
}
} // namespace AMF
